package org.xtext.runtime.plan;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.interpreter.Agent;
import org.xtext.runtime.term.ITerm;
import org.xtext.runtime.term.ListConstructor;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.type.TypeException;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.EvaluateTermsVisitor;

public class AssignmentStep implements IPlanStep {
	private Variable variable;
	private ITerm expression;
		
	public AssignmentStep(Variable variable, ITerm expression) {
		this.variable = variable;
		this.expression = expression;
	}
	
	public String toString() {
		return toString("");
	}
	
	public String toString(String tabs) {
		return tabs + variable + "=" + expression;
	}
	
	public String toFullString(String tabs) {
		return toString(tabs);
	}

	public ITerm variable() {
		return variable;
	}
	
	public ITerm expression() {
		return expression;
	}

	@Override
	public void handle(Agent agent, PlanStack planStack) {
		ITerm exp = (ITerm) expression.accept(new EvaluateTermsVisitor(agent));

		try {
			if (ListConstructor.class.isInstance(exp)) {
				exp = ((ListConstructor) exp).construct(agent);
			}
			
			variable.assign(exp);

			agent.logAction(variable + "=" + exp+ " [ASSIGNED]");
		} catch (TypeException e) {
			planStack.fail();
			agent.logAction(variable + "=" + exp+ " [FAILED TO ASSIGN]");
		}
	}

	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		variables.add(this.variable);
		return variables;
	}

	@Override
	public AssignmentStep copy(Map<String, Variable> vars) {
		Variable var = vars.get(variable.identifier());
		if (var == null) {
			// When this happens, it is because the variable is defined
			// in a higher scope than the copy operation.  Because of this,
			// we simply add the variable to the map and do not copy it
			// so that we keep the link to the higher scope declaration...
			vars.put(variable.identifier(), variable);
			var = variable;
		}
		return new AssignmentStep(var, expression.copy(vars));
	}

	@Override
	public AssignmentStep apply(Bindings bindings) {
		return this;
	}

	@Override
	public void reset() {
	}
}
